package com.yoke.nginxlog;

import com.alibaba.druid.util.JdbcUtils;
import com.github.odiszapc.nginxparser.NgxConfig;
import com.github.odiszapc.nginxparser.NgxParam;
import com.yoke.database.DatabaseContextHolder;
import com.yoke.system.SystemBean;
import com.yoke.system.log.LogType;
import com.yoke.system.log.SystemLog;
import com.yoke.util.StringUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.http.HttpMethod;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by jiangzeyin on 2017/8/8.
 */
public class ReadLogJob implements Job {
    private static final ThreadLocal<HashMap<String, String>> THREAD_LOCAL = new ThreadLocal<>();
    private static final List<String> DO_FILE = new ArrayList<>();

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String confPath = SystemBean.getInstance().getEnvironment().getProperty("nginx.conf");
        SystemLog.LOG().info("开始分析日志");
        try {
            HashMap<String, String> hashMap = THREAD_LOCAL.get();
            if (hashMap == null) {
                hashMap = new HashMap<>();
                THREAD_LOCAL.set(hashMap);
            }
            start(confPath);
            hashMap.clear();
        } catch (Exception e) {
            SystemLog.ERROR().error("分析日志异常", e);
        }
        SystemLog.LOG().info("分析日志结束");
    }

    private void doLogFile(File file, String doMain) throws IOException {
        if (DO_FILE.contains(file.getPath())) {
            SystemLog.LOG().info("上次：" + file.getPath() + " 还没有处理结束");
            return;
        }
        try {
            DO_FILE.add(file.getPath());
            SystemLog.LOG().info("开始解析：" + file.getPath());
            DataSource dataSource = DatabaseContextHolder.getDataSource();
            FileReader reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            String str;
            while ((str = br.readLine()) != null) {
                String line = str;
                int s = str.indexOf(" ");
                // ip
                String ip = str.substring(0, s);
                str = str.substring(s);
                // time
                s = str.indexOf("[");
                int e = str.indexOf("]");
                String time = str.substring(s + 1, e);
                SimpleDateFormat format = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z", Locale.ENGLISH);
                int timeInt = 0;
                try {
                    timeInt = (int) (format.parse(time).getTime() / 1000L);
                } catch (ParseException ignored) {
                }
                str = str.substring(e + 2);
                // type
                s = str.indexOf("\"");
                e = str.indexOf(" ");
                String type = str.substring(s + 1, e);
                // url
                str = str.substring(e + 1);
                s = str.indexOf(" ");
                String url = str.substring(0, s);
                if ("/favicon.ico".equalsIgnoreCase(url))
                    continue;
                if ("/".equals(url)) {
                    url = "/index.html";
                } else if (url.endsWith("/")) {
                    url = url + "index.html";
                } else {
                    if (url.contains("?")) {
                        String prx = url.substring(0, url.indexOf("?"));
                        if (prx.endsWith("/")) {
                            prx += "index.html";
                            url = prx + url.substring(url.indexOf("?"));
                        } else if (prx.contains(".")) {
                            String newPrx = url.substring(prx.lastIndexOf("."));
                            if (!".html".equals(newPrx))
                                continue;
                        } else {
                            System.out.println("prx:" + prx + "  " + url);
                        }
                    } else {
                        if (!url.contains(".")) {
                            continue;
                        }
                        String prx = url.substring(url.lastIndexOf("."));
                        if (!".html".equals(prx))
                            continue;
                    }
                }
                url = doMain + url;
                // status
                str = str.substring(s + 1);
                s = str.indexOf(" ");
                str = str.substring(s + 1);
                s = str.indexOf(" ");
                String status = str.substring(0, s);
                // referer
                str = str.substring(s + 1);
                s = str.indexOf("\"");
                str = str.substring(s + 1);
                s = str.indexOf("\"");
                String referer = str.substring(0, s);
                // uag
                str = str.substring(s + 1);
                s = str.indexOf("\"");
                str = str.substring(s + 1);
                s = str.indexOf("\"");
                String uag = str.substring(0, s);
                // x-ip
                str = str.substring(s + 1);
                s = str.indexOf("\"");
                e = str.lastIndexOf("\"");
                String xIp = str.substring(s + 1, e);
                String sql = "INSERT INTO nginxAdLog(ip,xip,time,type,url,status,referer,uag) VALUES(?,?,?,?,?,?,?,?);";
                List<Object> parameters = new ArrayList<>();
                parameters.add(ip);
                parameters.add(xIp);
                parameters.add(timeInt);
                HttpMethod httpMethod = HttpMethod.resolve(StringUtil.convertNULL(type).toUpperCase());
                if (httpMethod == null) {
                    SystemLog.LOG().info("请求类型解析失败：" + type + "  :" + line);
                }
                parameters.add(type);
                parameters.add(url);
                int statusInt = StringUtil.parseInt(status, -200);
                if (statusInt == -200) {
                    SystemLog.LOG().info(" 状态码解析失败：" + status + "   :" + line);
                }
                parameters.add(statusInt);
                parameters.add(referer);
                parameters.add(uag);
                try {
                    JdbcUtils.execute(dataSource, sql, parameters);
                } catch (Exception e1) {
                    SystemLog.LOG(LogType.CONTROL_ERROR).error("同步失败：" + line, e1);
                }
            }
            br.close();
            reader.close();
            if (!file.delete()) {
                SystemLog.LOG().info(file.getPath() + "删除失败");
            }
        } finally {
            DO_FILE.remove(file.getPath());
        }
    }

    private void start(String path) throws IOException {
        File file = new File(path);
        Stack<File> stack = new Stack<>();
        stack.push(file);
        // 遍历类路径
        while (!stack.isEmpty()) {
            File item = stack.pop();
            File[] subFile = item.listFiles((dir, name) -> new File(dir, name).isDirectory() || name.endsWith(".conf"));
            if (subFile != null)
                for (File itemFile : subFile) {
                    if (itemFile.isDirectory())
                        stack.push(itemFile);
                    else {
                        scannerFile(itemFile);
                    }
                }
        }
    }

    private void scannerFile(File file) throws IOException {
        NgxConfig conf = NgxConfig.read(file.getPath());
        NgxParam access_log = conf.findParam("server", "access_log");
        String[] logInfo = StringUtil.StringToArray(access_log.getValue(), " ");
        if (logInfo == null) {
            SystemLog.LOG().info(file.getPath() + " 日志配置解析失败");
            return;
        }
        File logFile = new File(logInfo[0]);
        if (!logFile.exists() || !logFile.isFile()) {
            SystemLog.LOG().info(file.getPath() + " 日志文件不正确：" + logFile.getPath());
            return;
        }
        NgxParam server_name = conf.findParam("server", "server_name");
        if (server_name == null) {
            SystemLog.LOG().info(file.getPath() + " 没有配置域名");
            return;
        }
        List<String> stringList = server_name.getValues();
        if (stringList == null || stringList.size() < 1) {
            SystemLog.LOG().info(file.getPath() + " 没有配置域名");
            return;
        }
        HashMap<String, String> hashMap = THREAD_LOCAL.get();
        if (hashMap != null) {
            if (hashMap.containsKey(file.getPath())) {
                SystemLog.LOG().info(file.getPath() + " 存在重复读取");
                return;
            }
            if (hashMap.containsValue(logFile.getPath())) {
                SystemLog.LOG().info(file.getPath() + " 的 " + logFile.getPath() + " 配置重复");
                return;
            }
            hashMap.put(file.getPath(), logFile.getPath());
        }
        String doMain = stringList.get(0);
        String logName = logFile.getName();
        SystemLog.LOG().info("处理：" + doMain + "  " + logName);
        File[] file_backs = logFile.getParentFile().listFiles((dir, name) -> !new File(dir, name).isDirectory() && !name.endsWith(".log") && name.startsWith(logName));
        if (file_backs != null) {
            for (File item : file_backs)
                doLogFile(item, "http://" + doMain);
        }
    }
}

